#include "Interface.h"

#include <iostream>

NAMESPACE_BEGIN
std::int64_t Interface::next_mac = 1;

std::mutex Interface::next_mac_mtx;

void Interface::setInterfaceType(const InterfaceType& interface_type)
{
	std::unique_lock<std::shared_mutex> lock(this->interface_shared_mtx);
	this->interface_type = interface_type;
}

bool Interface::setIPV4(const std::string& ipv4)
{
	std::uint32_t new_ipv4 = 0;
	try
	{
		new_ipv4 = turnIPV4StrToUint32(ipv4);
	}
	catch (const std::exception& e)
	{
		ENS_LOG___ERROR(e.what());
		return false;
	}
	std::unique_lock<std::shared_mutex> lock(this->interface_shared_mtx);
	this->ipv4 = new_ipv4;
	return true;
}

bool Interface::setIPV4Mask(const std::string& ipv4_mask)
{
	std::uint32_t new_ipv4_mask = 0;
	try
	{
		new_ipv4_mask = turnIPV4StrToUint32(ipv4_mask);
	}
	catch (const std::exception& e)
	{
		ENS_LOG___ERROR(e.what());
		return false;
	}
	std::unique_lock<std::shared_mutex> lock(this->interface_shared_mtx);
	this->ipv4_mask = new_ipv4_mask;
	return true;
}

bool Interface::setIPV4Gate(const std::string& ipv4_gate)
{
	std::uint32_t new_ipv4_gate = 0;
	try
	{
		new_ipv4_gate = turnIPV4StrToUint32(ipv4_gate);
	}
	catch (const std::exception& e)
	{
		ENS_LOG___ERROR(e.what());
		return false;
	}
	std::unique_lock<std::shared_mutex> lock(this->interface_shared_mtx);
	this->ipv4_gate = new_ipv4_gate;
	return true;
}

bool Interface::setIPV6(const std::string& ipv6)
{
	IPV6 new_ipv6{};
	try
	{
		new_ipv6 = turnIPV6StrToIPV6Structure(ipv6);
	}
	catch (const std::exception& e)
	{
		ENS_LOG___ERROR(e.what());
		return false;
	}
	std::unique_lock<std::shared_mutex> lock(this->interface_shared_mtx);
	this->ipv6 = new_ipv6;
	return true;
}

bool Interface::setIPV6Mask(const std::string& ipv6_mask)
{
	IPV6 new_ipv6_mask{};
	try
	{
		new_ipv6_mask = turnIPV6StrToIPV6Structure(ipv6_mask);
	}
	catch (const std::exception& e)
	{
		ENS_LOG___ERROR(e.what());
		return false;
	}
	std::unique_lock<std::shared_mutex> lock(this->interface_shared_mtx);
	this->ipv6_mask = new_ipv6_mask;
	return true;
}

bool Interface::setIPV6Gate(const std::string& ipv6_gate)
{
	IPV6 new_ipv6_gate{};
	try
	{
		new_ipv6_gate = turnIPV6StrToIPV6Structure(ipv6_gate);
	}
	catch (const std::exception& e)
	{
		ENS_LOG___ERROR(e.what());
		return false;
	}
	std::unique_lock<std::shared_mutex> lock(this->interface_shared_mtx);
	this->ipv6 = new_ipv6_gate;
	return true;
}

void Interface::setMAC(const std::string& mac)
{
	std::size_t n = mac.size();
	std::int64_t new_mac = 0;
	for (std::int32_t i = 0; i < n; i++)
	{
		if (mac[i] != ':')
		{
			std::int32_t cur_bits = 0;
			if (mac[i] >= '0' && mac[i] <= '9')
			{
				cur_bits = (mac[i] - '0');
			}
			else if (mac[i] >= 'a' && mac[i] <= 'f')
			{
				cur_bits = 10 + (mac[i] - 'a');
			}
			else if (mac[i] >= 'A' && mac[i] <= 'F')
			{
				cur_bits = 10 + (mac[i] - 'A');
			}
			else
			{
				ENS_LOG___ERROR("Incorrect MAC code.");
				return;
			}
			new_mac <<= 4;
			new_mac |= cur_bits;
		}
	}
	if (new_mac >= 0x1000000000000)
	{
		ENS_LOG___ERROR("Incorrect MAC code.");
	}
	else
	{
		std::unique_lock<std::shared_mutex> lock(this->interface_shared_mtx);
		this->mac = new_mac;
	}
}

void Interface::setParentDevice(Device* device)
{
	std::unique_lock<std::shared_mutex> lock(this->interface_shared_mtx);
	this->device = device;
}

std::unique_lock<std::shared_mutex> Interface::uniqueLock()
{
	return std::unique_lock<std::shared_mutex>(this->interface_shared_mtx);
}

std::string Interface::getIPV4Str()
{
	std::shared_lock<std::shared_mutex> lock(this->interface_shared_mtx);
	std::string ans;
	ans += std::to_string(this->ipv4 >> 24);
	ans += '.';
	ans += std::to_string((this->ipv4 >> 16) & 0x00ff);
	ans += '.';
	ans += std::to_string((this->ipv4 >> 8) & 0x0000ff);
	ans += '.';
	ans += std::to_string(this->ipv4 & 0x000000ff);
	return ans;
}

std::uint32_t Interface::getIPV4()
{
	std::shared_lock<std::shared_mutex> lock(this->interface_shared_mtx);
	return this->ipv4;
}

std::string Interface::getIPV6Str()
{
	static std::string assist = "0123456789ABCDEF";
	std::shared_lock<std::shared_mutex> lock(this->interface_shared_mtx);
	std::string ans;
	std::uint64_t assist_num = 0xf000000000000000;
	for (std::int32_t i = 15; i >=0; i--)
	{
		ans += assist[(this->ipv6.pre & assist_num) >> (i * 4)];
		if (i % 4 == 0)ans += ':';
		assist_num >>= 4;
	}
	assist_num <<= 60;
	for (std::int32_t i = 15; i >= 0; i--)
	{
		ans += assist[(this->ipv6.suf & assist_num) >> (i * 4)];
		if (i % 4 == 0 && i != 0)ans += ':';
		assist_num >>= 4;
	}
	return ans;
}

std::string Interface::getMACStr()
{
	static std::string assist = "0123456789ABCDEF";
	std::shared_lock<std::shared_mutex> lock(this->interface_shared_mtx);
	std::string ans;
	std::uint64_t assist_num = 0x0000f00000000000;
	for (std::int32_t i = 11; i >= 0; i--)
	{
		ans += assist[(this->mac & assist_num) >> (i * 4)];
		if (i % 2 == 0 && i != 0)ans += ':';
		assist_num >>= 4;
	}
	return ans;
}

std::uint32_t Interface::getMAC()
{
	std::shared_lock<std::shared_mutex> lock(this->interface_shared_mtx);
	return this->mac;
}

bool Interface::checkConnected()
{
	std::shared_lock<std::shared_mutex> lock(this->interface_shared_mtx);
	return this->connected_interface != nullptr;
}

Interface* Interface::getConnectedInterface()
{
	std::shared_lock<std::shared_mutex> lock(this->interface_shared_mtx);
	return this->connected_interface;
}

Device* Interface::getParentDevice()
{
	std::shared_lock<std::shared_mutex> lock(this->interface_shared_mtx);
	return this->device;
}

bool Interface::connectToAnotherInterface(Interface* another_interface)
{
	{
		std::shared_lock<std::shared_mutex> lock(this->interface_shared_mtx);
		if (this->connected_interface != nullptr) return false;
	}
	std::unique_lock<std::shared_mutex> lock(this->interface_shared_mtx);
	this->connected_interface = another_interface;
	return true;
}

bool Interface::disconnectToAnotherInterface()
{
	{
		std::shared_lock<std::shared_mutex> lock(this->interface_shared_mtx);
		if (this->connected_interface == nullptr) return false;
	}
	std::unique_lock<std::shared_mutex> lock(this->interface_shared_mtx);
	this->connected_interface = nullptr;
	return true;
}

bool checkInterfaceStr(const std::string& interface_str)
{
	std::size_t n = interface_str.size();
	if (n < 4) return false;
	char interface_type_ch = interface_str[0];
	if (interface_type_ch == 'e' ||                // ethernet
		interface_type_ch == 'c' ||                // console
		interface_type_ch == 's' ||                // serial
		interface_type_ch == 'g' ||                // gigabit_ethernet
		interface_type_ch == 'f' ||                // fast_ethernet
		interface_type_ch == 'b')                  // bri
	{
		std::int32_t i = 1;
		while (i < n && interface_str[i] != '/')
		{
			if (!(interface_str[i] >= '0' && interface_str[i] <= '9'))
			{
				return false;
			}
			i++;
		}
		if (i == n) return false;
		i++;
		while (i < n)
		{
			if (!(interface_str[i] >= '0' && interface_str[i] <= '9'))
			{
				return false;
			}
			i++;
		}
		return true;
	}
	return false;
}

InterfaceDescriptor generateInterfaceDescriptor(const std::string& interface_str)
{
	std::size_t n = interface_str.size();
	char interface_type_ch = interface_str[0];
	InterfaceType interface_type = InterfaceType::Console;
	switch (interface_type_ch)
	{
	case 'e':
		interface_type = InterfaceType::Ethernet;
		break;
	case 's':
		interface_type = InterfaceType::Serial;
		break;
	case 'g':
		interface_type = InterfaceType::GigabitEthernet;
		break;
	case 'f':
		interface_type = InterfaceType::FastEthernet;
		break;
	case 'b':
		interface_type = InterfaceType::BRI;
		break;
	default:
		break;
	}
	std::int32_t i = 1;
	std::int32_t group_id = 0;
	while (i < n && interface_str[i] != '/')
	{
		group_id *= 10;
		group_id += (interface_str[i] - '0');
		i++;
	}
	i++;
	std::int32_t interface_id = 0;
	while (i < n)
	{
		interface_id *= 10;
		interface_id += (interface_str[i] - '0');
		i++;
	}
	return InterfaceDescriptor(interface_type, group_id, interface_id);
}

std::uint32_t turnIPV4StrToUint32(const std::string& ipv4)
{
	std::string assist;
	std::uint32_t new_ipv4 = 0;
	std::size_t n = ipv4.size();
	for (std::int32_t i = 0; i <= n; i++)
	{
		if (i < n && ipv4[i] != '.')
		{
			assist += ipv4[i];
		}
		else
		{
			try
			{
				new_ipv4 <<= 8;
				new_ipv4 |= std::stoi(assist);
				assist.clear();
			}
			catch (const std::exception& e)
			{
				throw std::exception((std::string("Error: ") + e.what()).c_str());
			}
		}
	}
	return new_ipv4;
}

IPV6 turnIPV6StrToIPV6Structure(const std::string& ipv6)
{
	std::size_t n = ipv6.size();
	IPV6 new_ipv6{};
	std::int32_t count = 0;
	for (std::int32_t i = 0; i < n; i++)
	{
		if (ipv6[i] != ':')
		{
			std::int32_t cur_bits = 0;
			if (ipv6[i] >= '0' && ipv6[i] <= '9')
			{
				cur_bits = (ipv6[i] - '0');
			}
			else if (ipv6[i] >= 'a' && ipv6[i] <= 'f')
			{
				cur_bits = 10 + (ipv6[i] - 'a');
			}
			else if (ipv6[i] >= 'A' && ipv6[i] <= 'F')
			{
				cur_bits = 10 + (ipv6[i] - 'A');
			}
			else
			{
				throw std::exception("Incorrect IPV6 code.");
			}
			if (count < 16)
			{
				new_ipv6.pre <<= 4;
				new_ipv6.pre |= cur_bits;
			}
			else
			{
				new_ipv6.suf <<= 4;
				new_ipv6.suf |= cur_bits;
			}
			count++;
		}
	}
	return new_ipv6;
}
NAMESPACE_END
